package com.chuangke.admin.service.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.constant.SysConstant;
import com.chuangke.admin.dao.SysRoleDaoService;
import com.chuangke.admin.data.builder.UserDataBuilder;
import com.chuangke.admin.entity.SysMenu;
import com.chuangke.admin.entity.SysRole;
import com.chuangke.admin.entity.SysRoleMenu;
import com.chuangke.admin.entity.SysUserAuditDept;
import com.chuangke.admin.entity.SysUserRole;
import com.chuangke.admin.service.SysMenuService;
import com.chuangke.admin.service.SysRoleMenuService;
import com.chuangke.admin.service.SysRoleService;
import com.chuangke.admin.service.SysUserAuditDeptService;
import com.chuangke.admin.service.SysUserRoleService;
import com.chuangke.common.constant.CacheKeyConstants;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.data.builder.DataBuildConfig;
import com.chuangke.common.data.builder.DataBuilder;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.db.page.PageUtil;
import com.chuangke.common.idgen.IdManager;
import com.chuangke.framework.util.TreeUtils;

import cn.hutool.core.util.StrUtil;

@Service
public class SysRoleServiceImpl implements SysRoleService {

    @Autowired
    private SysRoleMenuService sysRoleMenuService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysRoleDaoService sysRoleDaoService;

    @Autowired
    private SysUserAuditDeptService sysUserAuditDeptService;

    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void save(SysRole record) {
        sysRoleDaoService.saveOrUpdate(record);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void save(List<SysRole> records) {
        sysRoleDaoService.saveBatch(records);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void deleteByIds(List<String> ids) {
        sysRoleDaoService.removeByIds(ids);
    }

    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void delete(SysRole record) {// 删除角色时同时删除sys_user_audit_dept表及sys_role_dept表数据
        String roleId = record.getId();
        sysUserRoleService.deleteByRoleId(roleId);
        sysUserAuditDeptService
                .remove(new QueryWrapper<SysUserAuditDept>().lambda().eq(SysUserAuditDept::getRoleId, roleId));
        sysRoleDaoService.removeById(roleId);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void delete(List<SysRole> records) {
        for (SysRole record : records) {
            delete(record);
        }
    }

    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void saveRoleMenus(List<SysRoleMenu> records) {
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        
        List<String> menuIds = records.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList()) ;
        List<SysMenu> menuList = sysMenuService.findKinshipTree(menuIds) ;
        menuList = TreeUtils.treeToList(menuList) ;
        
        String roleId = records.get(0).getRoleId();
        sysRoleMenuService.deleteByRoleId(roleId);
        
        //2023.05.06增加，前端只返回明细级，上级自动获取。
        List<SysRoleMenu> list = menuList.stream().map(menu -> {
        	SysRoleMenu srm = new SysRoleMenu() ;
        	srm.setRoleId(roleId) ;
        	srm.setMenuId(menu.getId()) ;
        	return srm ;
        }).collect(Collectors.toList()) ;
        sysRoleMenuService.insert(list);
    }

    @Cacheable(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public Map<String, SysRole> findMap() {
        List<SysRole> list = sysRoleDaoService.list();
        return list.stream().collect(Collectors.toMap(SysRole::getId, a -> a));
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void saveRoleUsers(String roleId, List<String> userIdList) {// 删掉用户原有的角色，设置成新的角色
        sysUserRoleService.deleteByUserIds(userIdList);
        List<SysUserRole> list = new ArrayList<>();
        userIdList.forEach(item -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setId(IdManager.nextId());
            sysUserRole.setUserId(item);
            sysUserRole.setRoleId(roleId);
            list.add(sysUserRole);
        });
        sysUserRoleService.insert(list);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void updateDataRule(String roleId, String dataRule) {
        sysRoleDaoService.update(null,
                new UpdateWrapper<SysRole>().lambda().eq(SysRole::getId, roleId).set(SysRole::getDataRule, dataRule));
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_ROLE, key = "'" + CacheKeyConstants.SYS_ROLE + "'")
    @Override
    public void updateAuditRule(String roleId, String auditRule) {
        sysRoleDaoService.update(null,
                new UpdateWrapper<SysRole>().lambda().eq(SysRole::getId, roleId).set(SysRole::getAuditRule, auditRule));
    }

    @Override
    public List<SysRole> findRoles(List<String> roleIds) {
        List<SysRole> list = findList();
        List<SysRole> filterList = new ArrayList<>();
        list.forEach(item -> {
            String roleId = item.getId();
            if (roleIds.contains(roleId)) {
                filterList.add(item);
            }
        });
        return filterList;
    }

    @Override
    public List<SysRole> findList() {
        Map<String, SysRole> map = ((SysRoleService) AopContext.currentProxy()).findMap();
        return new ArrayList<>(map.values());
    }

    @Override
    public List<SysMenu> findRoleMenus(String roleId) {
        Map<String, SysRole> map = ((SysRoleService) AopContext.currentProxy()).findMap();
        SysRole sysRole = map.get(roleId);
        if (sysRole == null) {
            return null;
        }

        Map<String, SysMenu> allMenu = sysMenuService.findMap();
        if (SysConstant.ADMIN.equalsIgnoreCase(sysRole.getName())) {
            // 如果是超级管理员，返回全部
            return new ArrayList<>(allMenu.values());
        }

        List<SysRoleMenu> roleMenuList = sysRoleMenuService.findByRoleId(roleId);
        
        List<SysMenu> result = new ArrayList<>();

        if (roleMenuList == null || roleMenuList.isEmpty()) {
            return result;
        }

        for (SysRoleMenu sysRoleMenu : roleMenuList) {
        	SysMenu menu = allMenu.get(sysRoleMenu.getMenuId()) ;
        	if(menu == null) {
        		continue ;
        	}
        	
        	//2023.05.06增加，前端多选框联动，如果选择了上级，默认自动下级全部选中，所以不返回上级，由下级联动处理。
        	if(Masks.BOOL_NO.equals(menu.getIsLastLevel())) {
        		continue ;
        	}
        	
            result.add(menu==null ? menu:menu.clone());
        }
        return result;
    }

    @Override
    public List<SysRole> findByNameMatch(String name) {
        Map<String, SysRole> map = ((SysRoleService) AopContext.currentProxy()).findMap();
        List<SysRole> list = new ArrayList<>();
        for (SysRole role : map.values()) {
            if (role.getName().matches(".*" + name + ".*")) {
                list.add(role);
            }
        }
        return list;
    }

    @Override
    public SysRole findByName(String name) {
        Map<String, SysRole> map = ((SysRoleService) AopContext.currentProxy()).findMap();
        for (SysRole role : map.values()) {
            if (name.equals(role.getName())) {
                return role;
            }
        }
        return null;
    }

    @Override
    public SysRole findById(String id) {
        Map<String, SysRole> map = ((SysRoleService) AopContext.currentProxy()).findMap();
        return map.get(id);
    }

    @Override
    public IPage<SysRole> page(CkPageQuery page) {
        String fuzzy = (String) page.get("fuzzy");
        String type = (String) page.get("type");
        List<SysRole> list = ((SysRoleService) AopContext.currentProxy()).findList();
        list = list.stream().filter(role -> (StrUtil.isBlank(fuzzy)
                        || (StrUtil.isNotBlank(fuzzy) && (role.getRemarks().contains(fuzzy) || role.getName().contains(fuzzy)))))
                .collect(Collectors.toList());

        if(!StrUtil.isBlank(type)){
            list = list.stream().filter(role -> Objects.equals(type, role.getType())).collect(Collectors.toList());
        }

        DataBuilder.of(list).appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
                new DataBuildConfig("updatedBy", "updatedByName")).build();
        list.sort(Comparator.comparing(SysRole::getCreatedAt));
        return PageUtil.findPage(page, list);
    }

	@Override
	public List<SysRole> findByType(String type) {
		List<SysRole> allRoles = ((SysRoleService) AopContext.currentProxy()).findList() ;
		return allRoles.stream().filter(r -> Objects.equals(type, r.getType())).collect(Collectors.toList()) ;
	}

}
